﻿using System;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.WindowsAzure.StorageClient;
using System.Collections.Generic;
using AntsCode.StorageClientExtensions.Helpers;
using System.Data.Services.Client;

namespace AntsCode.StorageClientExtensions.QueryHandlers
{
    /// <summary>
    /// Handles an OrderBy query
    /// </summary>
    /// <typeparam name="T">The source type.</typeparam>
    /// <typeparam name="U">The return type.</typeparam>
    internal class OrderByQueryHandler<T, U> : QueryHandler
    {
        public OrderByQueryHandler(TableServiceContext context, QueryExecutor queryExecutor, Expression e, MethodCallExpression m, IQueryable<T> data, ContinuationToken continuationToken, OrderByType orderByType)
            : base(data, continuationToken)
        {
            IQueryable<T> rawData;

            if (data == null)
            {
                string entitySetName = GetValueFromConstant<IXTableServiceQuery>(m.Arguments[0]).EntitySetName;
                rawData = context.CreateQuery<T>(entitySetName);
            }
            else
            {
                rawData = data;
            }

            if (rawData.GetType().IsSubclassOf(typeof(DataServiceQuery<T>)))
            {
                // Suffix any additional methods from the parent expression (First(), FirstOrDefault() etc)
                // This must be done before we execute the query for the methods to be applied to the Table Service Query.
                var query = SuffixQuery.SuffixExtras(e, (DataServiceQuery<T>)rawData);

                // Execute the query
                queryExecutor.IncrementQueryBatchNo();
                var response = queryExecutor.ExecuteAll(query, ref continuationToken); 

                // Update the continuation token
                this.ContinuationToken = continuationToken;

                rawData = response.ToList().AsQueryable();
            }

            // Get data
            var expr = GetExprFromUnary<Expression<Func<T, U>>>(m.Arguments[1]);

            switch (orderByType)
            {
                case OrderByType.OrderBy:
                    this.Data = rawData.OrderBy(expr);
                    break;

                case OrderByType.OrderByDescending:
                    this.Data = rawData.OrderByDescending(expr);
                    break;

                case OrderByType.ThenBy:
                    this.Data = ((IOrderedQueryable<T>)rawData).ThenBy(expr);
                    break;

                case OrderByType.ThenByDescending:
                    this.Data = ((IOrderedQueryable<T>)rawData).ThenByDescending(expr);
                    break;
            }
        }
    }
}